package cppweb.spring.ibatis;


import stdx.Utils;
import webx.WebApp;
import dbx.DBConnect;
import stdx.TaskQueue;
import java.util.List;
import stdx.BaseException;
import java.util.ArrayList;
import java.io.InputStream;
import javax.sql.DataSource;
import java.io.ByteArrayInputStream;
import org.springframework.core.io.Resource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.generator.config.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.apache.ibatis.datasource.pooled.PooledDataSource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

public class MybatisUtils{
	static int started = 0;
	static String generatorConfigFile = "/cppweb/spring/ibatis/generator.xml";

	public static void setGeneratorConfigFile(String path){
		generatorConfigFile = path;
	}

	public static List<String> getScriptList(String script){
		List<String> list = new ArrayList<>();
		String[] arr = script.replaceAll("\r\n", "\n").split(";\n");

		for (String item : arr){
			if (Utils.IsEmpty(item = item.trim())) continue;

			String sql = "";

			for (String line : item.split("\n")){
				if (Utils.IsEmpty(line = line.trim())) continue;

				if (line.startsWith("#") || line.startsWith("--")) continue;

				sql += line + " ";
			}

			int str = 0;
			int end = sql.trim().length() - 1;

			while (str <= end){
				if (sql.charAt(str) != ';') break;
				str++;
			}

			while (str <= end){
				if (sql.charAt(end) != ';') break;
				end--;
			}

			if (str <= end) list.add(sql.substring(str, end + 1));
		}

		return list;
	}

	public static String translateDatabaseName(String poolname){
        return Utils.IsEmpty(poolname) || poolname.toLowerCase().equals("default") ? null : poolname;
    }

    public static PooledDataSource createDataSource(String poolname){
        final long delay = 1000;
        final String name = translateDatabaseName(poolname);
        final PooledDataSource source = new PooledDataSource();

        TaskQueue.Instance().push(new Runnable(){
            public void run(){
                for (int i = 0; i < 10; i++){
                    DBConnect.Config cfg = DBConnect.GetPool(name).getConfig();

                    if (Utils.IsNotEmpty(cfg) && Utils.IsNotEmpty(cfg.url)){
                        source.setPassword(cfg.password);
                        source.setUsername(cfg.user);
                        source.setDriver(cfg.driver);
                        source.setUrl(cfg.url);
                        break;
                    }

                    Utils.Sleep(delay);
                }
            }
        }, delay);

        return source;
    }

	public static DBConnect.Config initDatabase(String poolname) throws Exception{
		if (started <= 0){
			started = BaseException.OK.getErrorCode();
			TaskQueue.Instance().push(new Runnable(){
				public void run(){
					try {
						String appname = WebApp.CheckConfig();
						Utils.SetEnv("SKIP_LOAD_WEBAPP", "1");
						WebApp.Process(appname);
						started = 0;
					}
					catch (Exception e){
						started = BaseException.ERROR.getErrorCode();
						e.printStackTrace();
					}
				}
			});
		}

		if (Utils.IsEmpty(poolname) || poolname.toLowerCase().equals("default")) poolname = null;

		for (int i = 0; i < 10; i++){
			DBConnect.Config cfg = DBConnect.GetPool(poolname).getConfig();
			if (Utils.IsEmpty(cfg) || Utils.IsEmpty(cfg.url)){
				Utils.Sleep(1000);
			}
			else{
				WebApp.SetLogFlag(0);
				return cfg;
			}
		}

		if (Utils.IsEmpty(poolname)){
			throw BaseException.SYSERR.copy("initialize default database failed");
		}
		else{
			throw BaseException.SYSERR.copy("initialize database[" + poolname + "] failed");
		}
	}

	public static int executeScript(String poolname, String script) throws Exception{
		return executeScript(poolname, script, true);
	}

	public static int executeScript(String poolname, String script, boolean autocommit) throws Exception{
		List<String> list = getScriptList(script);

		if (Utils.IsEmpty(list)) return 0;

		DBConnect dbconn = DBConnect.Connect(poolname, autocommit);

		try{
			for (String sql : list){
				dbconn.execute(sql);
			}
			dbconn.commit();
			return list.size();
		}
		catch (Exception e){
			if (autocommit) throw e;
			dbconn.rollback();
			throw e;
		}
		finally{
			dbconn.close();
		}
	}

	public static SqlSessionFactory createSessionFactory(DataSource datasource, Class clazz) throws Exception{
		MapperScan scan = (MapperScan)clazz.getAnnotation(MapperScan.class);

		if (scan == null || scan.basePackages() == null) throw BaseException.ERROR.copy("database srouce config error");

		String[] arr = scan.basePackages();
		String[] paths = new String[arr.length];

		for (int i = 0; i < arr.length; i++){
			paths[i] = "classpath:" + arr[i].replaceAll("[.]", "/") + "/*.xml";
		}

		return createSessionFactory(datasource, paths);
	}
    public static SqlSessionFactory createSessionFactory(DataSource datasource, String... patterns) throws Exception{
        List<Resource> list = new ArrayList<Resource>();

        for (int i = 0; i < patterns.length; i++){
            Resource[] resources = new PathMatchingResourcePatternResolver().getResources(patterns[i]);

            for (int j = 0; j < resources.length; j++){
                list.add(resources[j]);
            }
        }

        Resource[] resources = new Resource[list.size()];

        for (int i = 0; i < list.size(); i++){
            resources[i] = list.get(i);
        }

        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setMapperLocations(resources);
        bean.setDataSource(datasource);

        org.apache.ibatis.session.Configuration cfg = new org.apache.ibatis.session.Configuration();
        cfg.setLogImpl(MybatisLogFile.class);
        bean.setConfiguration(cfg);

        return bean.getObject();
    }

    public static Configuration getGeneratorConfig(String poolname, String pattern, String targetpackage) throws Exception{
		int pos = 0;
        DBConnect.Config cfg = initDatabase(poolname);
        String msg = Utils.GetResourceString(MybatisUtils.class, generatorConfigFile);

		pos = cfg.url.indexOf('?');
        msg = msg.replace("${pattern}", pattern);
        msg = msg.replace("${jdbc.user}", cfg.user);
        msg = msg.replace("${jdbc.driver}", cfg.driver);
		msg = msg.replace("${jdbc.password}", cfg.password);
        msg = msg.replace("${jdbc.connection}", pos > 0 ? cfg.url.substring(0, pos) : cfg.url);

        msg = msg.replace("${package}", targetpackage);
        msg = msg.replace("${project}", Utils.GetCurrentPath());

        List<String> warnings = new ArrayList<>();
        InputStream file = new ByteArrayInputStream(msg.getBytes());
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration etc = cp.parseConfiguration(file);
        file.close();

        for (String warning : warnings){
            System.out.println(warning);
        }

        return etc;
    }
}